package com.lzy.wzy.netty.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.google.gson.Gson;
import com.lzy.wzy.constant.MyConstant;
import com.lzy.wzy.mapper.TemplateMapper;
import com.lzy.wzy.mapper.UserMapper;
import com.lzy.wzy.mapper.WorkMapper;
import com.lzy.wzy.model.UIDAndAuthority;
import com.lzy.wzy.netty.msg.*;
import com.lzy.wzy.utils.BusinessUtils;
import com.lzy.wzy.utils.JsonUtil;
import com.lzy.wzy.utils.WorkOrderUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class ExecutorHandler {
    @Autowired
    private WorkOrderUtils workOrderUtils;

    @Autowired
    private WorkMapper workMapper;

    @Autowired
    private TemplateMapper templateMapper;

    @Autowired
    private BusinessUtils businessUtils;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private Gson gson;

    @Autowired
    private JsonUtil jsonUtil;

    @Autowired
    private TokenStore jwtTokenStoreRe;


    private final Random random = new Random();

    private final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");

    private final ThreadPoolExecutor executor = new ThreadPoolExecutor(5
            , 10
            , 0L
            , TimeUnit.SECONDS
            , new LinkedBlockingQueue<>(40)
            ,new  ThreadPoolExecutor.CallerRunsPolicy()
    );


    public void execute(PublicRequestMessage requestMessage, ChannelHandlerContext channelHandlerContext,String msg){
        CompletableFuture.runAsync(()->{
            String operation=requestMessage.getOperation();
            String workOrderNumber=requestMessage.getWorkOrderNumber();
            if (userMapper.getApproveStatus(workOrderNumber,1) > 0) {
                channelHandlerContext.writeAndFlush(businessUtils.otherError("审核中,停止生产"));
                return;
            }
            UIDAndAuthority uidAndAuthority = checkToken(requestMessage.getToken());
            if (uidAndAuthority == null) {
                channelHandlerContext.writeAndFlush(businessUtils.otherError("身份信息已过期，请重新登录"));
                return;
            }

            String userId = uidAndAuthority.getUserId();
            int templateId=requestMessage.getTemplateId();
            String status = workMapper.getWorkOrderStatus(workOrderNumber);
            List<String> authority = uidAndAuthority.getAuthority();

            String roomId = workOrderNumber + templateId;

            String id;
            switch (operation) {
                case MyConstant.ProductionOperation.ADD:
                    if (checkProduct(status, MyConstant.PO.OTHER_ERROR, authority, channelHandlerContext.channel())) {
                        return;
                    }
                    if (!checkTotal(workOrderNumber, templateId)) {
                        channelHandlerContext.writeAndFlush(businessUtils.otherError("生产已达标，请勿再添加"));
                        return;
                    }
                    if (MyConstant.WorkOrderStatus.RELEASE.equals(status)) {
                        businessUtils.doExecute(workOrderNumber);
                    }
                    String oldId=workOrderNumber + LocalDateTime.now().format(dtf) + String.format("%02d", random.nextInt(100) + 1);
                    try {
                        id = workOrderUtils.encode(oldId);
                    } catch (Exception e) {
                        channelHandlerContext.writeAndFlush(businessUtils.otherError("非法id"));
                        return;
                    }
                    AddResponseMessage responseMessage = new AddResponseMessage();
                    responseMessage.setId(id);
                    responseMessage.setOperation(operation);
                    responseMessage.setTemplateId(templateId);
                    responseMessage.setWorkOrderNumber(workOrderNumber);
                    businessUtils.doExecute(templateId, oldId, "id", workOrderNumber, userId, roomId, responseMessage);
                    return;
                case MyConstant.ProductionOperation.MODIFY:
                    if (checkProduct(status, MyConstant.PO.ERROR, authority, channelHandlerContext.channel())) {
                        return;
                    }
                    ModifyRequestMessage modifyRequestMessage = jsonUtil.jsonToPojo(msg, ModifyRequestMessage.class);
                    id = modifyRequestMessage.getId();
                    businessUtils.doExecute(id, modifyRequestMessage.getData(), modifyRequestMessage.getField(), authority, templateId, workOrderNumber, userId, roomId);
                    break;
                case MyConstant.ProductionOperation.DEL:
                    if (checkProduct(status, MyConstant.PO.OTHER_ERROR, authority, channelHandlerContext.channel())) {
                        return;
                    }
                    DeleteRequestMessage deleteRequestMessage = jsonUtil.jsonToPojo(msg, DeleteRequestMessage.class);
                    id = deleteRequestMessage.getId();
                    if (StringUtils.isBlank(id)) {
                        channelHandlerContext.writeAndFlush(businessUtils.otherError("id不能为空"));
                        return;
                    }
                    businessUtils.doExecute(templateId, id, workOrderNumber, userId, roomId, operation);
                    return;
                case MyConstant.ProductionOperation.CONNECTION:
                    businessUtils.doExecute(roomId, channelHandlerContext, userId ,workOrderNumber,authority);
                    return;
                default:
                    throw new IllegalStateException("Unexpected value: " + operation);
            }
        },executor);
    }

    private boolean checkTotal(String workOrderNumber, int templateId) {
        synchronized (this) {
            String tableName = templateMapper.queryTableNameByTemplateId(templateId);
            Integer count = templateMapper.getMachinesCountByWorkOrderNumber(tableName, workOrderNumber);
            Integer quantity = workMapper.getProductQuantityByWorkOrderNumber(workOrderNumber);
            return count < quantity;
        }
    }

    private boolean checkProduct(String status, String operation, List<String> authority, Channel channel) {
        if (!authority.contains(MyConstant.ApproveGroup.executorG) || MyConstant.WorkOrderStatus.FINISHED.equals(status)
                || MyConstant.WorkOrderStatus.CASECLOSE.equals(status)
                || MyConstant.WorkOrderStatus.MAJOR_EXCEPTION.equals(status)) {
            channel.writeAndFlush(new TextWebSocketFrame(gson.toJson(new ErrorResponseMessage(operation,"不可编辑"))));
            return true;
        }
        return false;
    }

    private UIDAndAuthority checkToken(String token) {
        token = token.substring(7);
        OAuth2AccessToken oAuth2AccessToken = jwtTokenStoreRe.readAccessToken(token);
        if (oAuth2AccessToken.isExpired()) {
            log.info("token 已过期");
            return null;
        }
        Map<String, Object> additionalInformation = oAuth2AccessToken.getAdditionalInformation();
        return new UIDAndAuthority(additionalInformation.get("userUuid").toString(), JSON.parseObject(JSON.toJSONString(additionalInformation.get("authorities")), new TypeReference<ArrayList<String>>() {
        }));
    }
}
